import os
from pathlib import Path
from typing import Optional
from urllib.parse import unquote

import pytest
from pytest import CaptureFixture

from gemini_antenna.cgi import customFilters
from gemini_antenna.db import AntennaDB
from gemini_antenna.multiFeedParsing import parsegemsub

script_name = "/~antenna/cgi-bin/filter"

filter_ask_input = "10 Type 'y' if you want to create a filter, otherwise go back.\r\n"
filter_ask_rule_add = "10 Rule to add:\r\n"
filter_ask_rule_remove = "10 Rule to remove:\r\n"


def add_rule(filter_name: str, url: str) -> None:
    os.environ['PATH_INFO'] = f"/{filter_name}/add"
    os.environ['QUERY_STRING'] = url
    customFilters.main()


def remove_rule(filter_name: str, url: str) -> None:
    os.environ['PATH_INFO'] = f"/{filter_name}/remove"
    os.environ['QUERY_STRING'] = url
    customFilters.main()


def read_rules(filter_name: str) -> None:
    os.environ['PATH_INFO'] = f"/{filter_name}/read"
    customFilters.main()


@pytest.mark.parametrize("path_info, query",
                         [(None, None), ("/", ""), ("/foo", "n")])
def test_create_ask_input(path_info: Optional[str], query: Optional[str],
                          capfd: CaptureFixture[str]):
    os.environ['SCRIPT_NAME'] = pytest.filter_script_name

    if query is not None:
        os.environ['QUERY_STRING'] = query
    elif 'QUERY_STRING' in os.environ:
        del os.environ['QUERY_STRING']

    if path_info is not None:
        os.environ['PATH_INFO'] = path_info
    elif 'PATH_INFO' in os.environ:
        del os.environ['PATH_INFO']

    customFilters.main()
    assert capfd.readouterr().out == filter_ask_input


@pytest.mark.parametrize("query", ["ye", "yes"])
def test_create_success(db: AntennaDB, query: str, capfd: CaptureFixture[str]):
    os.environ['SCRIPT_NAME'] = pytest.filter_script_name
    os.environ['QUERY_STRING'] = query

    customFilters.main()
    out: str = capfd.readouterr().out

    match = pytest.filter_redirect.match(out)
    assert match

    filter_name = match.group(1)
    assert Path(db.dbPath.parent / "customfilters" / filter_name).exists()


@pytest.mark.parametrize("query", [None, ""])
def test_add_ask_input(db: AntennaDB, filter_name: str, query: str,
                       capfd: CaptureFixture[str]):
    with open(Path("tests") / "feed.gmi") as file:
        entries = parsegemsub(file.read(),
                              "gemini://gemini.jrandom.net/gemlog/")
    db.insertFeedEntries(entries)

    # test reading rules before anything is filtered
    read_rules(filter_name)
    out: str = capfd.readouterr().out
    assert out.startswith("20 text/gemini\r")
    assert out.endswith(
f"""
=> add?gemini://gemini.jrandom.net/gemlog/ Click to block 'gemini://gemini.jrandom.net/gemlog/'

=> add Click to add custom rule
""")

    if query is not None:
        os.environ['QUERY_STRING'] = query
    elif 'QUERY_STRING' in os.environ:
        del os.environ['QUERY_STRING']

    os.environ['PATH_INFO'] = f"/{filter_name}/add"
    customFilters.main()
    assert capfd.readouterr().out == filter_ask_rule_add


@pytest.mark.parametrize("url", [
    "gemini://example.com/badfeed",
    "gemini%3A%2F%2Fexample.com%2Fbadfeed",
])
def test_add_success(filter_name: str, url: str, capfd: CaptureFixture[str]):
    add_rule(filter_name, url)
    out: str = capfd.readouterr().out
    assert pytest.filter_redirect.match(out)

    url = unquote(url)
    read_rules(filter_name)
    out: str = capfd.readouterr().out
    assert (out ==
f"""20 text/gemini\r
# Your Filter Rules

=> remove?{url} {url} (Click to remove)


=> add Click to add custom rule
""")


@pytest.mark.parametrize("query", [None, ""])
def test_remove_ask_input(filter_name: str, query: str,
                          capfd: CaptureFixture[str]):
    os.environ['PATH_INFO'] = f"/{filter_name}/remove"

    if query is not None:
        os.environ['QUERY_STRING'] = query
    elif 'QUERY_STRING' in os.environ:
        del os.environ['QUERY_STRING']

    customFilters.main()
    assert capfd.readouterr().out == filter_ask_rule_remove


def test_remove_success(filter_name: str, capfd: CaptureFixture[str]):
    urls = [
        "gemini://example.com/goodfeed",
        "gemini%3A%2F%2Fexample.com%2Fgoodfeed",
    ]

    for url in urls:
        add_rule(filter_name, url)
        out: str = capfd.readouterr().out
        assert pytest.filter_redirect.match(out)

        url = unquote(url)
        read_rules(filter_name)
        out: str = capfd.readouterr().out
        assert f"=> remove?{url} {url} (Click to remove)" in out

    for url in urls:
        remove_rule(filter_name, url)
        out: str = capfd.readouterr().out
        assert pytest.filter_redirect.match(out)

        url = unquote(url)
        read_rules(filter_name)
        out: str = capfd.readouterr().out
        assert f"=> remove?{url} {url} (Click to remove)" not in out

    remove_rule(filter_name, "gemini://example.org/feed")
    out: str = capfd.readouterr().out
    assert pytest.filter_redirect.match(out)


def test_filtered_feed(db: AntennaDB, filter_name: str,
                       capfd: CaptureFixture[str]):
    with open(Path("tests") / "feed.gmi") as file:
        entries = parsegemsub(file.read(),
                              "gemini://gemini.jrandom.net/gemlog/")
    db.insertFeedEntries(entries)

    os.environ['PATH_INFO'] = f"/{filter_name}"
    customFilters.main()
    out: str = capfd.readouterr().out
    assert out.startswith(
f"""20 text/gemini\r
=> {pytest.filter_script_name}/{filter_name}/read Configure your filter.

# Your Filtered Feed

=> gemini://gemini.jrandom.net/gemlog/bokashi.gmi 2020-11-20 J. Random Geminaut's gemlog: - Early Bokashi composting experiments


=> gemini://gemini.jrandom.net/gemlog/finite-simple-groups.gmi 2020-11-13 J. Random Geminaut's gemlog: - Trying to get to grips with finite simple groups...


=> gemini://gemini.jrandom.net/gemlog/balcony.gmi 2020-11-06 J. Random Geminaut's gemlog: - I started a balcony garden!

> """)

    add_rule(filter_name, "gemini://gemini.jrandom.net/gemlog/")
    capfd.readouterr()

    os.environ['PATH_INFO'] = f"/{filter_name}"
    customFilters.main()
    out: str = capfd.readouterr().out
    assert out.startswith(
f"""20 text/gemini\r
=> {pytest.filter_script_name}/{filter_name}/read Configure your filter.

# Your Filtered Feed
>""")
